package org.jasper.impl.management;

import org.jasper.api.knowledge.InternalKnowledgeBase;
import org.jasper.api.runtime.KieRuntimeEventManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Created by OP038074 on 2018/7/10.
 */
public class DroolsManagementAgent {
    protected static final transient Logger logger = LoggerFactory.getLogger(DroolsManagementAgent.class);
    private static final String CONTAINER_NAME_PREFIX = "org.kie";
    private static DroolsManagementAgent INSTANCE;
    private static MBeanServer mbs;
    private long kbases;
    private long ksessions;
    private Map<Object, List<ObjectName>> mbeans;
    private Map<Object, Object> mbeansRefs = new HashMap<Object, Object>();

    private DroolsManagementAgent() {
        kbases = 0;
        ksessions = 0;
        mbeans = new HashMap<Object, List<ObjectName>>();
    }

    public static synchronized DroolsManagementAgent getInstance() {
        if ( INSTANCE == null ) {
            INSTANCE = new DroolsManagementAgent();
        }
        return INSTANCE;
    }

    public static ObjectName createObjectNameBy(String containerId) {
        return DroolsManagementAgent.createObjectName(CONTAINER_NAME_PREFIX + ":kcontainerId="+ObjectName.quote(containerId));
    }

    public static ObjectName createObjectName(String name) {
        try {
            return new ObjectName( name );
        } catch ( Exception e ) {
            logger.error( "This is a bug. Error creating ObjectName for MBean: " + name
                    + "\nPlease contact the development team and provide the following stack trace: " + e.getMessage(), e);
            return null;
        }
    }

    public void registerMBean(Object owner,
                              Object mbean,
                              ObjectName name) {
        try {
            MBeanServer mbs = getMBeanServer();
            if ( !mbs.isRegistered( name ) ) {
                mbs.registerMBean( mbean,
                        name );
                List<ObjectName> mbl = mbeans.get( owner );
                if ( mbl == null ) {
                    mbl = new LinkedList<ObjectName>();
                    mbeans.put( owner,
                            mbl );
                    if (mbean instanceof StandardMBean) {
                        mbeansRefs.put(owner, ((StandardMBean) mbean).getImplementation());
                    } else {
                        mbeansRefs.put(owner, mbean);
                    }
                }
                mbl.add( name );
                logger.debug( "Registered {} into the platform MBean Server", name );
            }
        } catch ( Exception e ) {
            logger.error( "Unable to register mbean " + name + " into the platform MBean Server", e );
        }
    }

    private static MBeanServer getMBeanServer() {
        if ( mbs == null ) {
            mbs = ManagementFactory.getPlatformMBeanServer();
        }
        return mbs;
    }

    public void registerKnowledgeSessionUnderName(CBSKey cbsKey, KieRuntimeEventManager ksession) {

    }

    public void unregisterKnowledgeSessionBean(CBSKey cbsKey) {
        unregisterMBeansFromOwner(cbsKey);
    }

    public void unregisterKnowledgeBase(InternalKnowledgeBase kbase) {
        unregisterMBeansFromOwner(kbase);
    }

    public void unregisterMBeansFromOwner(Object owner) {
        List<ObjectName> mbl = mbeans.remove( owner );
        mbeansRefs.remove(owner);
        if ( mbl != null ) {
            MBeanServer mbs = getMBeanServer();
            for ( ObjectName name : mbl ) {
                unregisterMBeanFromServer( mbs,
                        name );
            }
        }
    }

    private void unregisterMBeanFromServer(MBeanServer mbs,
                                           ObjectName name) {
        try {
            mbs.unregisterMBean( name );
            logger.debug( "Unregistered from MBean Server: {}", name);
        } catch ( Exception e ) {
            logger.error( "Exception unregistering mbean: " + name, e);
        }
    }

    public static class CBSKey {
        private final String kcontainerId;
        private final String kbaseId;
        private final String ksessionName;
        public CBSKey(String kcontainerId, String kbaseId, String ksessionName) {
            super();
            this.kcontainerId = kcontainerId;
            this.kbaseId = kbaseId;
            this.ksessionName = ksessionName;
        }

        public String getKcontainerId() {
            return kcontainerId;
        }

        public String getKbaseId() {
            return kbaseId;
        }

        public String getKsessionName() {
            return ksessionName;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((kbaseId == null) ? 0 : kbaseId.hashCode());
            result = prime * result + ((kcontainerId == null) ? 0 : kcontainerId.hashCode());
            result = prime * result + ((ksessionName == null) ? 0 : ksessionName.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (!(obj instanceof CBSKey))
                return false;
            CBSKey other = (CBSKey) obj;
            if (kbaseId == null) {
                if (other.kbaseId != null)
                    return false;
            } else if (!kbaseId.equals(other.kbaseId))
                return false;
            if (kcontainerId == null) {
                if (other.kcontainerId != null)
                    return false;
            } else if (!kcontainerId.equals(other.kcontainerId))
                return false;
            if (ksessionName == null) {
                if (other.ksessionName != null)
                    return false;
            } else if (!ksessionName.equals(other.ksessionName))
                return false;
            return true;
        }

        @Override
        public String toString() {
            return "CBSKey [kcontainerId=" + kcontainerId + ", kbaseId=" + kbaseId + ", ksessionName=" + ksessionName + "]";
        }

    }
}
